/*=====================================================================

  File:        SSPI.h

---------------------------------------------------------------------

This file is part of the Microsoft MSDN Code Samples.

  Copyright (C) Microsoft Corporation 2002.  All rights reserved.

This source code is intended only as a supplement to Microsoft
Development Tools and/or on-line documentation.  See these other
materials for detailed information regarding Microsoft code samples.

THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

=====================================================================*/

#pragma once

// ------------------------------ ** ------------------------------
//
//                             Includes
//
// ------------------------------ ** ------------------------------

#include "Exception.h"




// ------------------------------ ** ------------------------------
//
//                         Using Statements
//
// ------------------------------ ** ------------------------------

using namespace System;
using namespace System::Collections::Specialized;





// ------------------------------ ** ------------------------------
//
//                             Constants
//
// ------------------------------ ** ------------------------------

#define TOKENBUFSIZE					12288
#define szNegotiatePackageName			__TEXT("Negotiate")
#define szKerberosPackageName			__TEXT("Kerberos")
#define szNTLMPackageName				__TEXT("NTLM")
#define STANDARD_CONTEXT_ATTRIBUTES		ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONNECTION





namespace Microsoft
{
namespace Samples
{
namespace Security
{
namespace SSPI
{
	// ------------------------------ ** ------------------------------
	//
	//                            Credential
	//
	// ------------------------------ ** ------------------------------

	public __gc __abstract class Credential : public IDisposable
	{
	public:

		// types
		__value enum Package {Negotiate, Kerberos, NTLM}; 
		__value enum CredentialType {Client, Server};



		// constructor/destructor
		Credential(Package package, CredentialType credentialType) 
		{
			// we're not disposed yet
			this->disposed = false;


			// create a new credential handle
			// note: we're allocating the CredHandle on the C++ heap.  This needs to be
			// explicitly deleted upon Disposal
			this->credentialHandle = __nogc new CredHandle;
			this->credentialHandle->dwLower = 0; this->credentialHandle->dwUpper = 0;


			// capture the package
			this->securityPackage = package;


			// determine package name for the call the AcquireCredentialsHandle
			TCHAR *pszPackageName = NULL;
			switch (package)
			{
			case Package::Negotiate:
				pszPackageName = szNegotiatePackageName;
				break;

			case Package::Kerberos:
				pszPackageName = szKerberosPackageName;
				break;

			case Package::NTLM:
				pszPackageName = szNTLMPackageName;
				break;
			}


			// determine credential use
			ULONG fCredentialUse = 0;
			switch (credentialType)
			{
			case CredentialType::Client:
				fCredentialUse = SECPKG_CRED_OUTBOUND;
				break;

			case CredentialType::Server:
				fCredentialUse = SECPKG_CRED_INBOUND;
				break;
			}


			// acquire credentials handle
			TimeStamp tsExpiry = { 0, 0 };
			SECURITY_STATUS sResult = AcquireCredentialsHandle(
					NULL,											// [in] name of principal. NULL = principal of current security context
					pszPackageName,									// [in] name of package
					fCredentialUse,									// [in] flags indicating use.
					NULL,											// [in] pointer to logon identifier.  NULL = we're not specifying the id of another logon session
					NULL,											// [in] package-specific data.  NULL = default credentials for security package
					NULL,											// [in] pointer to GetKey function.  NULL = we're not using a callback to retrieve the credentials
					NULL,											// [in] value to pass to GetKey
					this->credentialHandle,							// [out] credential handle (this must be already allocated)
					&tsExpiry										// [out] lifetime of the returned credentials
			);


			// check for errors
			if (sResult != SEC_E_OK)
			{
				SSPIException *ex = new SSPIException(S"AcquireCredentialsHandle failed", sResult);
				throw ex;
			}
		}

		~Credential()
		{
			// Do not re-create Dispose clean-up code here.
			// Calling Dispose(false) is optimal in terms of
			// readability and maintainability.
			Dispose(false);
		}

		// Implement IDisposable*
		// Do not make this method virtual.
		// A derived class should not be able to call this method.
		void Dispose() 
		{
			Dispose(true);
			// Take ourselves off the Finalization queue 
			// to prevent finalization code for this object
			// from executing a second time.
			GC::SuppressFinalize(this);
		}



	protected:

		// Dispose(bool disposing) executes in two distinct scenarios.
		// If disposing equals true, the method has been called directly
		// or indirectly by a user's code. Managed and unmanaged resources
		// can be disposed.
		// If disposing equals false, the method has been called by the
		// runtime from inside the finalizer and you should not reference
		// other objects. Only unmanaged resources can be disposed.
		virtual void Dispose(bool disposing) 
		{
			// Check to see if Dispose has already been called.
			if (!this->disposed) 
			{
				// If disposing equals true, dispose all managed
				// and unmanaged resources.
				if (disposing) 
				{
					// Dispose managed resources

					// (there are no managed resources here)
				}


				// Call the appropriate methods to clean up
				// unmanaged resources here.
				// If disposing is false,
				// only the following code is executed.


				// clean up
				SECURITY_STATUS sResult = FreeCredentialsHandle(
					this->credentialHandle							// [in] handle to free
				);


				// delete the memory allocated for the handle
				delete this->credentialHandle;
				this->credentialHandle = NULL;


				// check for errors
				if (sResult != SEC_E_OK)
				{
					SSPIException *ex = new SSPIException(S"FreeCredentialsHandle failed", sResult);
					throw ex;
				}
			}
			this->disposed = true;
		}



	public:

		// properties
		__property Package get_SecurityPackage()
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			return this->securityPackage;
		};

		__property IntPtr get_CredentialHandle()
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			return IntPtr(this->credentialHandle);
		};

		__property String *get_Name()
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			String *name = NULL;


			// get the name associated with this credential
			SecPkgCredentials_Names secPkgCredentials_Names = { 0 };


			try
			{
				SECURITY_STATUS sResult = QueryCredentialsAttributes(this->credentialHandle, SECPKG_CRED_ATTR_NAMES, &secPkgCredentials_Names);


				// check for errors
				if (sResult != SEC_E_OK)
				{
					SSPIException *ex = new SSPIException(S"QueryCredentialsAttributes failed", sResult);
					throw ex;
				}


				// copy the name for the caller
				name = secPkgCredentials_Names.sUserName;
			}
			__finally
			{
				// free the buffer
				if (secPkgCredentials_Names.sUserName != NULL)
				{
					FreeContextBuffer(secPkgCredentials_Names.sUserName);
					secPkgCredentials_Names.sUserName = NULL;
				}
			}


			// return the name to the caller
			return name;
		};



	private:

		// member variables
		CredHandle __nogc* credentialHandle;
		Package securityPackage;
		bool disposed;
	};





	// ------------------------------ ** ------------------------------
	//
	//                         ClientCredential
	//
	// ------------------------------ ** ------------------------------

	public __gc class ClientCredential : public Credential
	{
	public:

		// constructor/destructor
		ClientCredential(Package package) : Credential(package, ClientCredential::Client)
		{
		}

	};





	// ------------------------------ ** ------------------------------
	//
	//                         ServerCredential
	//
	// ------------------------------ ** ------------------------------

	public __gc class ServerCredential : public Credential
	{
	public:

		// constructor/destructor
		ServerCredential(Package package) : Credential(package, ClientCredential::Server)
		{
		}

	};





	// ------------------------------ ** ------------------------------
	//
	//                              Context
	//
	// ------------------------------ ** ------------------------------

	public __gc __abstract class Context : public IDisposable
	{
	public:		
		
		// constructor/destructor
		Context(Credential *credential)
		{
			// we're not disposed yet
			this->disposed = false;


			// initialize members
			this->token = NULL;
			this->continueProcessing = true;
			this->contextAttributes = 0;


			// create a new context handle
			// note: we're allocating the CtxtHandle on the C++ heap.  This needs to be
			// explicitly deleted upon Disposal
			this->contextHandle = __nogc new CtxtHandle;
			this->contextHandle->dwLower = 0; this->contextHandle->dwUpper = 0;


			// hang onto a reference to the associated credential
			this->credential = credential;
		}

		~Context()
		{
			// Do not re-create Dispose clean-up code here.
			// Calling Dispose(false) is optimal in terms of
			// readability and maintainability.
			Dispose(false);
		}

		// Implement IDisposable*
		// Do not make this method virtual.
		// A derived class should not be able to call this method.
		void Dispose() 
		{
			Dispose(true);
			// Take ourselves off the Finalization queue 
			// to prevent finalization code for this object
			// from executing a second time.
			GC::SuppressFinalize(this);
		}



	protected:

		// Dispose(bool disposing) executes in two distinct scenarios.
		// If disposing equals true, the method has been called directly
		// or indirectly by a user's code. Managed and unmanaged resources
		// can be disposed.
		// If disposing equals false, the method has been called by the
		// runtime from inside the finalizer and you should not reference
		// other objects. Only unmanaged resources can be disposed.
		virtual void Dispose(bool disposing) 
		{
			// Check to see if Dispose has already been called.
			if (!this->disposed) 
			{
				// If disposing equals true, dispose all managed
				// and unmanaged resources.
				if (disposing) 
				{
					// Dispose managed resources

					// credential
					this->credential->Dispose();
					this->credential = NULL;
				}


				// Call the appropriate methods to clean up
				// unmanaged resources here.
				// If disposing is false,
				// only the following code is executed.


				// clean up
				SECURITY_STATUS sResult = DeleteSecurityContext(
					this->contextHandle											// [in] handle to free
				);


				// delete the memory allocated for the handle
				delete this->contextHandle;
				this->contextHandle = NULL;


				// check for errors
				if (sResult != SEC_E_OK)
				{
					SSPIException *ex = new SSPIException(S"DeleteSecurityContext failed", sResult);
					throw ex;
				}
			}
			this->disposed = true;
		}



	public:

		// methods
		Byte SignMessage(Byte msg[]) []
		{
			// this takes the given byte array, signs it, and returns another
			// byte array containing the original message and signature.
			// the format of the returned byte array is as follows:
			// |MESSAGE_LENGTH|MESSAGE|SIGNATURE|
			

			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// get the size of the message
			DWORD cbMsg = msg->Length;


			// get the size of the message length
			DWORD cbMsgLength = sizeof(cbMsg);


			// get the maximum size of a signature
			SecPkgContext_Sizes sizes;
			QueryContextAttributes(this->contextHandle, SECPKG_ATTR_SIZES, &sizes);


			// allocate space for the signed message
			Byte signedMsg[] = new Byte[cbMsgLength + cbMsg + sizes.cbMaxSignature];
			BYTE __pin* pSignedMsg = &signedMsg[0]; 


			// copy the message length and message into the signed message buffer
			BYTE __pin *pMsg = &msg[0];
			CopyMemory(pSignedMsg, &cbMsg, cbMsgLength);
			CopyMemory(pSignedMsg+cbMsgLength, pMsg, cbMsg);
			pMsg = NULL;


			// prepare the message buffer
			SecBuffer rgsb[] = {
				{cbMsg,					SECBUFFER_DATA,  pSignedMsg + cbMsgLength},
				{sizes.cbMaxSignature,	SECBUFFER_TOKEN, pSignedMsg + cbMsg + cbMsgLength},
			};
			SecBufferDesc sbd = {SECBUFFER_VERSION, sizeof rgsb / sizeof *rgsb, rgsb};


			// sign the message
			SECURITY_STATUS sResult = MakeSignature(
				this->contextHandle,					// context to use
				0,										// quality of protection
				&sbd,									// message to sign
				0										// message sequence number
			);


			// release pinned objects
			pSignedMsg = NULL;


			// check for errors
			if (sResult != SEC_E_OK)
			{
				SSPIException *ex = new SSPIException(S"MakeSignature failed", sResult);
				throw ex;
			}


			// return signed message to caller
			return signedMsg;
		}


		Byte VerifyMessage(Byte signedMsg[]) []
		{
			// this takes the given byte array and verifies it using the SSPI
			// VerifySignature method.  the given byte array is assumed to be of the format:
			// |MESSAGE_LENGTH|MESSAGE|SIGNATURE|


			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// get a pinned reference to parameters appropriate to call unmanaged methods
			BYTE __pin* pSignedMsg = &signedMsg[0]; 


			// get the size of the message length
			DWORD cbMsgLength = sizeof(DWORD);


			// get the size of the original message
			DWORD cbMsg = 0;
			CopyMemory(&cbMsg, pSignedMsg, cbMsgLength);


			// get the size of the signature
			const DWORD cbSignature = signedMsg->Length - cbMsg - cbMsgLength;


			// prepare the message buffer
			SecBuffer rgsb[] = {
				{cbMsg,					SECBUFFER_DATA,  pSignedMsg + cbMsgLength},
				{cbSignature,			SECBUFFER_TOKEN, pSignedMsg + cbMsg + cbMsgLength},
			};
			SecBufferDesc sbd = {SECBUFFER_VERSION, sizeof rgsb / sizeof *rgsb, rgsb};


			// sign the message
			ULONG qop = 0;
			SECURITY_STATUS sResult = VerifySignature(
				this->contextHandle,					// context to use
				&sbd,									// message to verify
				0,										// message sequence number
				&qop									// quality of protection
			);


			// retrieve the verified message (which is the original message passed to SignMessage)
			Byte verifiedMsg[] = new Byte[cbMsg];
			BYTE __pin* pVerifiedMsg = &verifiedMsg[0]; 
			CopyMemory(pVerifiedMsg, pSignedMsg + cbMsgLength, cbMsg);
			pVerifiedMsg = NULL;


			// release pinned objects
			pSignedMsg = NULL;


			// check for errors
			if (sResult != SEC_E_OK)
			{
				SSPIException *ex = new SSPIException(S"VerifySignature failed", sResult);
				throw ex;
			}


			// return the verified message to the caller
			return verifiedMsg;
		}


		Byte EncryptMessage(Byte msg[]) []
		{
			// this takes the given byte array, encrypts it, and returns
			// another byte array containing the encrypted message.
			// the format of the returned byte array is as follows:
			// |MESSAGE_LENGTH|ENCRYPTED_MESSAGE|


			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// get the size of the message
			DWORD cbMsg = msg->Length;


			// get the size of the message length
			DWORD cbMsgLength = sizeof(cbMsg);


			// get the maximum size of a signature
			SecPkgContext_Sizes sizes;
			QueryContextAttributes(this->contextHandle, SECPKG_ATTR_SIZES, &sizes);


			// allocate space for the encrypted message
			Byte encryptedMsg[] = new Byte[cbMsgLength + cbMsg + sizes.cbSecurityTrailer];
			BYTE __pin* pEncryptedMsg = &encryptedMsg[0]; 


			// copy the message into the signed message buffer
			BYTE __pin *pMsg = &msg[0];
			CopyMemory(pEncryptedMsg, &cbMsg, cbMsgLength);
			CopyMemory(pEncryptedMsg+cbMsgLength, pMsg, cbMsg);
			pMsg = NULL;


			// prepare the message buffer
			SecBuffer rgsb[] = {
				{cbMsg,						SECBUFFER_DATA,  pEncryptedMsg + cbMsgLength},
				{sizes.cbSecurityTrailer,	SECBUFFER_TOKEN, pEncryptedMsg + cbMsgLength + cbMsg},
			};
			SecBufferDesc sbd = {SECBUFFER_VERSION, sizeof rgsb / sizeof *rgsb, rgsb};


			// sign the message
			SECURITY_STATUS sResult = ::EncryptMessage(
				this->contextHandle,					// context to use
				0,										// quality of protection
				&sbd,									// message to encrypt
				0										// message sequence number
			);


			// The encrypted message may have taken less space than was allocated.
			// It's important we return a correctly sized message to the caller.
			Byte sealedMsg[] = new Byte[cbMsgLength + cbMsg + rgsb[1].cbBuffer];
			BYTE __pin* pSealedMsg = &sealedMsg[0]; 
			CopyMemory(pSealedMsg, pEncryptedMsg, cbMsgLength + cbMsg + rgsb[1].cbBuffer);
			pSealedMsg = NULL;


			// release pinned objects
			pEncryptedMsg = NULL;


			// check for errors
			if (sResult != SEC_E_OK)
			{
				Exception *ex = new Exception(S"Context::EncryptMessage: EncryptMessage failed");
				throw ex;
			}


			// return signed message to caller
			return sealedMsg;
		}


		Byte DecryptMessage(Byte encryptedMsg[]) []
		{
			// this takes the given byte array, decrypts it, and returns
			// the original, unencrypted byte array.
			// the given byte array is assumed to be of the format:
			// |MESSAGE_LENGTH|ENCRYPTED_MESSAGE|


			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// get a pinned reference to parameters to the unmanaged API
			BYTE __pin* pEncryptedMsg = &encryptedMsg[0]; 


			// get the size of the message length
			DWORD cbMsgLength = sizeof(DWORD);


			// get the size of the original, unencrypted message
			DWORD cbMsg = 0;
			CopyMemory(&cbMsg, pEncryptedMsg, cbMsgLength);


			// get the size of the trailer
			const DWORD cbTrailer = encryptedMsg->Length - cbMsg - cbMsgLength;


			// prepare the message buffer
			SecBuffer rgsb[] = {
				{cbMsg,						SECBUFFER_DATA,  pEncryptedMsg + cbMsgLength},
				{cbTrailer,					SECBUFFER_TOKEN, pEncryptedMsg + cbMsgLength + cbMsg},
			};
			SecBufferDesc sbd = {SECBUFFER_VERSION, sizeof rgsb / sizeof *rgsb, rgsb};


			// sign the message
			ULONG qop = 0;
			SECURITY_STATUS sResult = ::DecryptMessage(
				this->contextHandle,					// context to use
				&sbd,									// message to decrypt
				0,										// expected sequence number
				&qop									// quality of protection
			);


			// retrieve the decrypted message
			Byte decryptedMsg[] = new Byte[cbMsg];
			BYTE __pin* pDecryptedMsg = &decryptedMsg[0]; 
			CopyMemory(pDecryptedMsg, pEncryptedMsg + cbMsgLength, cbMsg);
			pDecryptedMsg = NULL;


			// release pinned objects
			pEncryptedMsg = NULL;


			// check for errors
			if (sResult != SEC_E_OK)
			{
				Exception *ex = new Exception(S"Context::DecryptMessage: DecryptMessage failed");
				throw ex;
			}


			// return the decrypted message to the caller
			return decryptedMsg;
		}



	protected:

		// methods
		Byte SecBufferToByteArray(SecBuffer &outSecBuff) []
		{
			// helper function to copy SecBuffer buffers into byte arrays.  This is typically
			// used to populate this->token.

			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			Byte outBuff[] = NULL;

			if (outSecBuff.cbBuffer > 0)
			{
				outBuff = new Byte[outSecBuff.cbBuffer];
				Byte __pin *pOutBuff = &outBuff[0];						// pin array
				for (unsigned long i = 0;(i < outSecBuff.cbBuffer);i++)
					pOutBuff[i] = *((BYTE *)outSecBuff.pvBuffer + i);
				pOutBuff = NULL;										// unpin array
			}

			return outBuff;
		}




	public:

		// properties
		__property Credential *get_Credential()
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			return credential;
		}

		__property StringCollection *get_ContextAttributes()
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// only derived classes should have a valid ContextAttributes property
			return NULL;
		}

		__property String *get_Name()
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			String *name = NULL;


			// get the name associated with this context
			SecPkgContext_Names secPkgContext_Names = { 0 };


			try
			{
				SECURITY_STATUS sResult = QueryContextAttributes(this->contextHandle, SECPKG_ATTR_NAMES, &secPkgContext_Names);


				// check for errors
				if (sResult != SEC_E_OK)
				{
					SSPIException *ex = new SSPIException(S"QueryContextAttributes failed", sResult);
					throw ex;
				}


				// copy the name for the caller
				name = secPkgContext_Names.sUserName;
			}
			__finally
			{
				// free the buffer
				if (secPkgContext_Names.sUserName != NULL)
				{
					FreeContextBuffer(secPkgContext_Names.sUserName);
					secPkgContext_Names.sUserName = NULL;
				}
			}


			// return the name to the caller
			return name;
		};

		__property Credential::Package get_SecurityPackage()
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			Credential::Package securityPackage;


			// get the SSP package associated with this context
			SecPkgContext_PackageInfo secPkgContext_PackageInfo = { 0 };


			try
			{
				SECURITY_STATUS sResult = QueryContextAttributes(this->contextHandle, SECPKG_ATTR_PACKAGE_INFO, &secPkgContext_PackageInfo);


				// check for errors
				if (sResult != SEC_E_OK)
				{
					SSPIException *ex = new SSPIException(S"QueryContextAttributes failed", sResult);
					throw ex;
				}


				// copy the name for the caller
				String *securityPackageName = secPkgContext_PackageInfo.PackageInfo->Name;


				// map the name to an enumeration
				if (String::Compare(securityPackageName, S"NTLM", true) == 0)
					securityPackage = Credential::Package::NTLM;
				else if (String::Compare(securityPackageName, S"Kerberos", true) == 0)
					securityPackage = Credential::Package::Kerberos;
				else
				{
					SSPIException *ex = new SSPIException(S"Could not map security package name", sResult);
					throw ex;
				}
			}
			__finally
			{
				// free the buffer
				if (secPkgContext_PackageInfo.PackageInfo != NULL)
				{
					if (secPkgContext_PackageInfo.PackageInfo->Name != NULL)
					{
						FreeContextBuffer(secPkgContext_PackageInfo.PackageInfo->Name);
						secPkgContext_PackageInfo.PackageInfo->Name = NULL;
					}

					if (secPkgContext_PackageInfo.PackageInfo->Comment != NULL)
					{
						FreeContextBuffer(secPkgContext_PackageInfo.PackageInfo->Comment);
						secPkgContext_PackageInfo.PackageInfo->Comment = NULL;
					}

					FreeContextBuffer(secPkgContext_PackageInfo.PackageInfo);
					secPkgContext_PackageInfo.PackageInfo = NULL;
				}
			}

			// return the name to the caller
			return securityPackage;
		};

		__property Byte get_Token() []
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			return this->token;
		};

		__property Boolean get_ContinueProcessing()
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			return this->continueProcessing;
		}



	protected:

		// member variables
		Microsoft::Samples::Security::SSPI::Credential *credential;
		CtxtHandle __nogc* contextHandle;
		Byte token[];
		Boolean continueProcessing;
		ULONG contextAttributes;



	private:
		
		// member variables
		bool disposed;
	};





	// ------------------------------ ** ------------------------------
	//
	//                           ClientContext
	//
	// ------------------------------ ** ------------------------------

	public __gc class ClientContext : public Context
	{
	public:

		// type definitions
		[Flags]
		__value enum ContextAttributeFlags
		{
			None						= 0,
			Delegate					= 1,
			Identify					= 2,
			MutualAuthentication		= 4
		};



		// constructor/destructor
		ClientContext(ClientCredential *credential, String *serverPrincipal, ContextAttributeFlags contextAttributeFlags) : Context(credential)
		{
			// we're not disposed yet
			this->disposed = false;


			// if the caller sets the DELEGATE flag where the security package is NTLM,
			// throw an exception
			if ( (contextAttributeFlags & ContextAttributeFlags::Delegate) && 
				 (credential->SecurityPackage == Credential::Package::NTLM) )
			{
				DelegateNotSupportedByNTLMException *ex = new DelegateNotSupportedByNTLMException();
				throw ex;
			}


			// capture the server principal name
			this->serverPrincipalName = serverPrincipal;


			// prepare the output buffer
			SecBufferDesc outBuffDesc;
			SecBuffer outSecBuff;
			BYTE outBuff[TOKENBUFSIZE];

			outBuffDesc.ulVersion = SECBUFFER_VERSION;
			outBuffDesc.cBuffers = 1;
			outBuffDesc.pBuffers = &outSecBuff;

			outSecBuff.cbBuffer = TOKENBUFSIZE;
			outSecBuff.BufferType = SECBUFFER_TOKEN;
			outSecBuff.pvBuffer = outBuff;


			// output parameters
			ULONG reqContextAttributes = STANDARD_CONTEXT_ATTRIBUTES;
			TimeStamp tsLifeSpan = { 0, 0 };


			// add requested context attributes
			if (contextAttributeFlags & ContextAttributeFlags::Delegate)
				reqContextAttributes = reqContextAttributes | ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH;
			if (contextAttributeFlags & ContextAttributeFlags::MutualAuthentication)
				reqContextAttributes = reqContextAttributes | ISC_REQ_MUTUAL_AUTH;
			if (contextAttributeFlags & ContextAttributeFlags::Identify)
				reqContextAttributes = reqContextAttributes | ISC_REQ_IDENTIFY;


			// get a reference to the credential
			CredHandle *phCredential = (CredHandle *)credential->CredentialHandle.ToPointer();


			// get a pinned reference to types appropriate for calling unmanaged methods
			ULONG __pin *pulContextAttributes = &this->contextAttributes;
			const wchar_t __pin* pwszServerPrincipalName = NULL;
			if ((credential->SecurityPackage == Credential::Package::Kerberos) || (credential->SecurityPackage == Credential::Package::Negotiate))
				pwszServerPrincipalName = PtrToStringChars(serverPrincipalName); 


			// initialize the context
			SECURITY_STATUS sResult = InitializeSecurityContext(
				phCredential,										// [in] handle to the credentials
				NULL,												// [in/out] handle of partially formed context. Always NULL the first time through
				(SEC_WCHAR*)pwszServerPrincipalName,				// [in] name of the target of the context. Not needed by NTLM
				reqContextAttributes,								// [in] required context attributes
				0,													// [reserved] reserved; must be zero
				SECURITY_NATIVE_DREP,								// [in] data representation on the target
				NULL,												// [in/out] pointer to the input buffers.  Always NULL the first time through
				0,													// [reserved] reserved; must be zero
				this->contextHandle,								// [in/out] receives the new context handle (must be pre-allocated)
				&outBuffDesc,										// [out] pointer to the output buffers
				pulContextAttributes,								// [out] receives the context attributes
				&tsLifeSpan											// [out] receives the life span of the security context
			);


			// we're finished with the credential pointer
			phCredential = NULL;


			// release pinned objects
			pulContextAttributes = NULL;
			pwszServerPrincipalName = NULL;


			// check for errors
			if (sResult == SEC_E_OK)
				continueProcessing = false;
			else if (sResult == SEC_I_CONTINUE_NEEDED)
				continueProcessing = true;
			else
			{
				SSPIException *ex = new SSPIException(S"InitializeSecurityContext failed.", sResult);
				throw ex;
			}


			// Set the token property
			this->token = SecBufferToByteArray(outSecBuff);
		}



	protected:

		virtual void Dispose(bool disposing)
		{
			if(!this->disposed)
			{
				try
				{
					if(disposing)
					{
						// Release the managed resources you added in
						// this derived class here.

						// (there are no managed resources in this class)
					}

					// Release the native unmanaged resources you added
					// in this derived class here.
					
					// (there are no unmanged resources in this class)

					this->disposed = true;
				}
				__finally
				{
					// Call Dispose on your base class.
					__super::Dispose(disposing);
				}
			}
		}



	public:

		// methods
		void Initialize(Byte inToken[])
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// prepare the input buffer
			SecBufferDesc inBuffDesc;
			SecBuffer inSecBuff;
			Byte __pin *pInToken = &inToken[0];

			inBuffDesc.ulVersion = SECBUFFER_VERSION;
			inBuffDesc.cBuffers = 1;
			inBuffDesc.pBuffers = &inSecBuff;

			inSecBuff.cbBuffer = inToken->Length;
			inSecBuff.BufferType = SECBUFFER_TOKEN;
			inSecBuff.pvBuffer = pInToken;


			// prepare the output buffer
			SecBufferDesc outBuffDesc;
			SecBuffer outSecBuff;
			BYTE outBuff[TOKENBUFSIZE];

			outBuffDesc.ulVersion = SECBUFFER_VERSION;
			outBuffDesc.cBuffers = 1;
			outBuffDesc.pBuffers = &outSecBuff;

			outSecBuff.cbBuffer = TOKENBUFSIZE;
			outSecBuff.BufferType = SECBUFFER_TOKEN;
			outSecBuff.pvBuffer = outBuff;


			// output parameters
			ULONG reqContextAttributes = STANDARD_CONTEXT_ATTRIBUTES;
			TimeStamp tsLifeSpan = { 0, 0 };


			// get a reference to the credential
			CredHandle *phCredential = (CredHandle *)credential->CredentialHandle.ToPointer();


			// get a pinned reference to the context
			ULONG __pin *pulContextAttributes = &this->contextAttributes;
			const wchar_t __pin* pwszServerPrincipalName = NULL; 
			if ((credential->SecurityPackage == Credential::Package::Kerberos) || (credential->SecurityPackage == Credential::Package::Negotiate))
				pwszServerPrincipalName = PtrToStringChars(serverPrincipalName); 


			// initialize the context
			SECURITY_STATUS sResult = InitializeSecurityContext(
				phCredential,										// [in] handle to the credentials
				this->contextHandle,								// [in/out] handle of partially formed context. Always NULL the first time through
				(SEC_WCHAR*)pwszServerPrincipalName,							// [in] name of the target of the context. Not needed by NTLM
				reqContextAttributes,								// [in] required context attributes
				0,													// [reserved] reserved; must be zero
				SECURITY_NATIVE_DREP,								// [in] data representation on the target
				&inBuffDesc,										// [in/out] pointer to the input buffers.  Always NULL the first time through
				0,													// [reserved] reserved; must be zero
				this->contextHandle,								// [in/out] receives the new context handle
				&outBuffDesc,										// [out] pointer to the output buffers
				pulContextAttributes,								// [out] receives the context attributes
				&tsLifeSpan											// [out] receives the life span of the security context
			);


			// we're finished with the credential pointer
			phCredential = NULL;


			// release pinned objects
			pInToken = NULL;
			pulContextAttributes = NULL;
			pwszServerPrincipalName = NULL;


			// check for errors
			if (sResult == SEC_E_OK)
				continueProcessing = false;
			else if (sResult == SEC_I_CONTINUE_NEEDED)
				continueProcessing = true;
			else
			{
				SSPIException *ex = new SSPIException(S"InitializeSecurityContext failed", sResult);
				throw ex;
			}


			// Set the token property
			this->token = SecBufferToByteArray(outSecBuff);
		}


		// properties
		__property StringCollection *get_ContextAttributes()
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// create a new collection
			StringCollection *contextAttributes = new StringCollection();


			// add all of the elements to the collection
			if (this->contextAttributes & ISC_RET_DELEGATE)
				contextAttributes->Add(S"Delegate");
			if (this->contextAttributes & ISC_RET_MUTUAL_AUTH)
				contextAttributes->Add(S"Mutual Authentication");
			if (this->contextAttributes & ISC_RET_REPLAY_DETECT )
				contextAttributes->Add(S"Replay Detection");
			if (this->contextAttributes & ISC_RET_SEQUENCE_DETECT)
				contextAttributes->Add(S"Sequence Detection");
			if (this->contextAttributes & ISC_RET_CONFIDENTIALITY)
				contextAttributes->Add(S"Confidentiality");
			if (this->contextAttributes & ISC_RET_USE_SESSION_KEY)
				contextAttributes->Add(S"Use Session Key");
			if (this->contextAttributes & ISC_RET_USED_COLLECTED_CREDS)
				contextAttributes->Add(S"Used collected credentials");
			if (this->contextAttributes & ISC_RET_USED_SUPPLIED_CREDS)
				contextAttributes->Add(S"Used Supplied Credentials");
			if (this->contextAttributes & ISC_RET_ALLOCATED_MEMORY)
				contextAttributes->Add(S"Allocated Memory");
			if (this->contextAttributes & ISC_RET_USED_DCE_STYLE)
				contextAttributes->Add(S"Used DCE Style");
			if (this->contextAttributes & ISC_RET_DATAGRAM)
				contextAttributes->Add(S"Datagram");
			if (this->contextAttributes & ISC_RET_CONNECTION)
				contextAttributes->Add(S"Connection");
			if (this->contextAttributes & ISC_RET_INTERMEDIATE_RETURN)
				contextAttributes->Add(S"Intermediate Return");
			if (this->contextAttributes & ISC_RET_CALL_LEVEL)
				contextAttributes->Add(S"Call Level");
			if (this->contextAttributes & ISC_RET_EXTENDED_ERROR)
				contextAttributes->Add(S"Extended Error");
			if (this->contextAttributes & ISC_RET_STREAM)
				contextAttributes->Add(S"Stream");
			if (this->contextAttributes & ISC_RET_INTEGRITY)
				contextAttributes->Add(S"Integrity");
			if (this->contextAttributes & ISC_RET_IDENTIFY)
				contextAttributes->Add(S"Identify");
			if (this->contextAttributes & ISC_RET_NULL_SESSION)
				contextAttributes->Add(S"NULL Session");
			if (this->contextAttributes & ISC_RET_MANUAL_CRED_VALIDATION)
				contextAttributes->Add(S"Manual Cred Validation");
			if (this->contextAttributes & ISC_RET_RESERVED1)
				contextAttributes->Add(S"Reserved1");
			if (this->contextAttributes & ISC_RET_FRAGMENT_ONLY)
				contextAttributes->Add(S"Fragment Only");


			// return the collection to the caller
			return contextAttributes;
		};



	private:

		// data members
		String *serverPrincipalName;
		bool disposed;

	};





	// ------------------------------ ** ------------------------------
	//
	//                           ServerContext
	//
	// ------------------------------ ** ------------------------------

	public __gc class ServerContext : public Context
	{
	public:

		// constructor/destructor
		ServerContext(ServerCredential *credential, Byte inToken[]) : Context(credential)
		{
			// we're not disposed yet
			this->disposed = false;


			// prepare the input buffer
			SecBufferDesc inBuffDesc;
			SecBuffer inSecBuff;
			Byte __pin *pInToken = &inToken[0];

			inBuffDesc.ulVersion = SECBUFFER_VERSION;
			inBuffDesc.cBuffers = 1;
			inBuffDesc.pBuffers = &inSecBuff;

			inSecBuff.cbBuffer = inToken->Length;
			inSecBuff.BufferType = SECBUFFER_TOKEN;
			inSecBuff.pvBuffer = pInToken;
		

			// prepare the output buffer
			SecBufferDesc outBuffDesc;
			SecBuffer outSecBuff;
			BYTE outBuff[TOKENBUFSIZE];

			outBuffDesc.ulVersion = SECBUFFER_VERSION;
			outBuffDesc.cBuffers = 1;
			outBuffDesc.pBuffers = &outSecBuff;

			outSecBuff.cbBuffer = TOKENBUFSIZE;
			outSecBuff.BufferType = SECBUFFER_TOKEN;
			outSecBuff.pvBuffer = outBuff;


			// output parameters
			ULONG reqContextAttributes = STANDARD_CONTEXT_ATTRIBUTES;
			TimeStamp tsLifeSpan = { 0, 0 };


			// get a reference to the credential
			CredHandle *phCredential = (CredHandle *)this->credential->CredentialHandle.ToPointer();


			// get a pinned reference to the context attributes
			ULONG __pin *pulContextAttributes = &this->contextAttributes;


			// initialize the context
			SECURITY_STATUS sResult = AcceptSecurityContext(
				phCredential,									// [in] handle to the credentials
				NULL,											// [in/out] handle of partially formed context.  Always NULL the first time through
				&inBuffDesc,									// [in] pointer to the input buffers
				reqContextAttributes,							// [in] required context attributes
				SECURITY_NATIVE_DREP,							// [in] data representation on the target
				this->contextHandle,							// [in/out] receives the new context handle	
				&outBuffDesc,									// [in/out] pointer to the output buffers
				pulContextAttributes,							// [out] receives the context attributes
				&tsLifeSpan										// [out] receives the life span of the security context
			);


			// release pinned objects
			phCredential = NULL;
			pInToken = NULL;
			pulContextAttributes = NULL;


			// check for errors
			if (sResult == SEC_E_OK)
				continueProcessing = false;
			else if (sResult == SEC_I_CONTINUE_NEEDED)
				continueProcessing = true;
			else
			{
				SSPIException *ex = new SSPIException(S"AcceptSecurityContext failed", sResult);
				throw ex;
			}


			// Set the token property
			this->token = SecBufferToByteArray(outSecBuff);
		}



	protected:

		virtual void Dispose(bool disposing)
		{
			if(!this->disposed)
			{
				try
				{
					if(disposing)
					{
						// Release the managed resources you added in
						// this derived class here.

						// (there are no managed resources in this class)
					}

					// Release the native unmanaged resources you added
					// in this derived class here.
					
					// (there are no unmanged resources in this class)

					this->disposed = true;
				}
				__finally
				{
					// Call Dispose on your base class.
					__super::Dispose(disposing);
				}
			}
		}



	public:

		// methods
		void Accept(Byte inToken[])
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// prepare the input buffer
			SecBufferDesc inBuffDesc;
			SecBuffer inSecBuff;
			Byte __pin *pInToken = &inToken[0];

			inBuffDesc.ulVersion = SECBUFFER_VERSION;
			inBuffDesc.cBuffers = 1;
			inBuffDesc.pBuffers = &inSecBuff;

			inSecBuff.cbBuffer = inToken->Length;
			inSecBuff.BufferType = SECBUFFER_TOKEN;
			inSecBuff.pvBuffer = pInToken;


			// prepare the output buffer
			SecBufferDesc outBuffDesc;
			SecBuffer outSecBuff;
			BYTE outBuff[TOKENBUFSIZE];

			outBuffDesc.ulVersion = SECBUFFER_VERSION;
			outBuffDesc.cBuffers = 1;
			outBuffDesc.pBuffers = &outSecBuff;

			outSecBuff.cbBuffer = TOKENBUFSIZE;
			outSecBuff.BufferType = SECBUFFER_TOKEN;
			outSecBuff.pvBuffer = outBuff;


			// output parameters
			ULONG reqContextAttributes = STANDARD_CONTEXT_ATTRIBUTES;
			TimeStamp tsLifeSpan = { 0, 0 };


			// get a reference to the credential
			CredHandle *phCredential = (CredHandle *)credential->CredentialHandle.ToPointer();


			// get a pinned reference to the context
			ULONG __pin *pulContextAttributes = &this->contextAttributes;


			// initialize the context
			SECURITY_STATUS sResult = AcceptSecurityContext(
				phCredential,									// [in] handle to the credentials
				this->contextHandle,							// [in/out] handle of partially formed context.  Always NULL the first time through
				&inBuffDesc,									// [in] pointer to the input buffers
				reqContextAttributes,							// [in] required context attributes
				SECURITY_NATIVE_DREP,							// [in] data representation on the target
				this->contextHandle,							// [in/out] receives the new context handle
				&outBuffDesc,									// [in/out] pointer to the output buffers
				pulContextAttributes,							// [out] receives the context attributes
				&tsLifeSpan										// [out] receives the life span of the security context
			);


			// we're finished with the credential pointer
			phCredential = NULL;


			// release pinned objects
			pInToken = NULL;
			pulContextAttributes = NULL;


			// check for errors
			if (sResult == SEC_E_OK)
				continueProcessing = false;
			else if (sResult == SEC_I_CONTINUE_NEEDED)
				continueProcessing = true;
			else
			{
				SSPIException *ex = new SSPIException(S"AcceptSecurityContext failed", sResult);
				throw ex;
			}


			// Set the token property
			this->token = SecBufferToByteArray(outSecBuff);
		}


		void ImpersonateClient(void)
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// impersonate the client
			SECURITY_STATUS sResult = ImpersonateSecurityContext(
				this->contextHandle										// [in] handle of context to impersonate
			);


			// check for errors
			if (sResult != SEC_E_OK)
			{
				SSPIException *ex = new SSPIException(S"ImpersonateSecurityContext failed", sResult);
				throw ex;
			}
		}

		void RevertImpersonation(void)
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// revert the impersonation
			SECURITY_STATUS sResult = RevertSecurityContext(
				this->contextHandle										// [in] handle of context to revert
			);


			// check for errors
			if (sResult != SEC_E_OK)
			{
				SSPIException *ex = new SSPIException(S"RevertSecurityContext failed", sResult);
				throw ex;
			}
		}



		// properties
		__property StringCollection *get_ContextAttributes()
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// create a new collection
			StringCollection *contextAttributes = new StringCollection();


			// add all of the elements to the collection
			if (this->contextAttributes & ASC_RET_DELEGATE)
				contextAttributes->Add(S"Delegate");
			if (this->contextAttributes & ASC_RET_MUTUAL_AUTH)
				contextAttributes->Add(S"Mutual Authentication");
			if (this->contextAttributes & ASC_RET_REPLAY_DETECT )
				contextAttributes->Add(S"Replay Detection");
			if (this->contextAttributes & ASC_RET_SEQUENCE_DETECT)
				contextAttributes->Add(S"Sequence Detection");
			if (this->contextAttributes & ASC_RET_CONFIDENTIALITY)
				contextAttributes->Add(S"Confidentiality");
			if (this->contextAttributes & ASC_RET_USE_SESSION_KEY)
				contextAttributes->Add(S"Use Session Key");
			if (this->contextAttributes & ASC_RET_ALLOCATED_MEMORY)
				contextAttributes->Add(S"Allocated Memory");
			if (this->contextAttributes & ASC_RET_USED_DCE_STYLE)
				contextAttributes->Add(S"Used DCE Style");
			if (this->contextAttributes & ASC_RET_DATAGRAM)
				contextAttributes->Add(S"Datagram");
			if (this->contextAttributes & ASC_RET_CONNECTION)
				contextAttributes->Add(S"Connection");
			if (this->contextAttributes & ASC_RET_CALL_LEVEL)
				contextAttributes->Add(S"Call Level");
			if (this->contextAttributes & ASC_RET_THIRD_LEG_FAILED)
				contextAttributes->Add(S"Third Leg Failed");
			if (this->contextAttributes & ASC_RET_EXTENDED_ERROR)
				contextAttributes->Add(S"Extended Error");
			if (this->contextAttributes & ASC_RET_STREAM)
				contextAttributes->Add(S"Stream");
			if (this->contextAttributes & ASC_RET_INTEGRITY)
				contextAttributes->Add(S"Integrity");
			if (this->contextAttributes & ASC_RET_LICENSING)
				contextAttributes->Add(S"Licensing");
			if (this->contextAttributes & ASC_RET_IDENTIFY)
				contextAttributes->Add(S"Identify");
			if (this->contextAttributes & ASC_RET_NULL_SESSION)
				contextAttributes->Add(S"NULL Session");
			if (this->contextAttributes & ASC_RET_ALLOW_NON_USER_LOGONS)
				contextAttributes->Add(S"Allow Non User Logons");
			if (this->contextAttributes & ASC_RET_ALLOW_CONTEXT_REPLAY)
				contextAttributes->Add(S"Allow Context Replay");
			if (this->contextAttributes & ASC_RET_FRAGMENT_ONLY)
				contextAttributes->Add(S"Fragment Only");


			// return the collection to the caller
			return contextAttributes;
		};

		__property IntPtr get_ClientSecurityToken()
		{
			if(this->disposed)
				throw new ObjectDisposedException(this->GetType()->Name);


			// retrieve the token
			HANDLE hToken = NULL;
			SECURITY_STATUS sResult = QuerySecurityContextToken(this->contextHandle, &hToken);


			// check for errors
			if (sResult != SEC_E_OK)
			{
				SSPIException *ex = new SSPIException(S"QuerySecurityContextToken failed", sResult);
				throw ex;
			}


			// return the handle to the caller
			return IntPtr(hToken);
		};



	private:

		// data members
		bool disposed;

	};





	// ------------------------------ ** ------------------------------
	//
	//                         Security Packages
	//
	// ------------------------------ ** ------------------------------

	public __gc class SecurityPackages
	{
	public:

		// constructor/destructor
		SecurityPackages()
		{
		}



		// methods
		static StringCollection *GetAllSecurityPackages()
		{
			// create a new collection
			StringCollection *packageNames = new StringCollection();
		

			// list of packages
			SecPkgInfo *ppSecPkgInfo = NULL;

			
			SECURITY_STATUS sRes = 0;

			try
			{
				// get the list of packages
				ULONG cPackages = 0;
				SECURITY_STATUS sResult = EnumerateSecurityPackages(&cPackages, &ppSecPkgInfo);


				// check for errors
				if (sResult != SEC_E_OK)
				{
					SSPIException *ex = new SSPIException(S"EnumerateSecurityPackages failed", sResult);
					throw ex;
				}


				// add the security package names to the collection
				for (ULONG i = 0;(i < cPackages);i++)
				{
					packageNames->Add(ppSecPkgInfo[i].Name);
					ULONG x = ppSecPkgInfo[i].cbMaxToken;
				}
			}
			__finally
			{
				// free memory
				if (ppSecPkgInfo != NULL)
				{
					sRes = FreeContextBuffer(ppSecPkgInfo);
					ppSecPkgInfo = NULL;
				}
			}


			// return the collection to the caller
			return packageNames;
		}

	};
}
}
}
}